package edu.gatech.ece6102;

import edu.gatech.ece6102.beans.AddressBean;
import edu.gatech.ece6102.beans.Node;

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 * User: deepal
 * Date: Nov 16, 2010
 * Time: 12:06:05 AM
 */

/**
 * This is the class which talks with FT-Manager and gather information about the system
 */
public class UDPClient extends Thread {
    // IP address of the FT manager
    private String faultManagerIP;
    //Port of the fault manager
    private int faultManagerPort;
    //Log based executor (to update about the application servers)
    private LogbasedExecutor logbasedExecutor;
    // port of the application server
    private int appServerPort;
    //a set of servers (current)
    private HashMap<String, Node> serverList;


    public UDPClient(String fmIP, int fmPort, int appServerPort, LogbasedExecutor logbasedExecutor) {
        faultManagerIP = fmIP;
        faultManagerPort = fmPort;
        this.logbasedExecutor = logbasedExecutor;
        this.appServerPort = appServerPort;
        serverList = new HashMap<String, Node>();
        start();
    }

    /**
     * For each second we send a message to FT-Manager and collect the information. In the future we will make the
     * sleeping time a configurable parameter
     */
    public void run() {
        while (true) {
            try {
                //creating datagram socket
                DatagramSocket clientSocket = new DatagramSocket();
                InetAddress IPAddress = InetAddress.getByName(faultManagerIP);
                byte[] receiveData = new byte[1024];
                String sentence = "data";
                byte[] sendData = sentence.getBytes();
                DatagramPacket sendPacket =
                        new DatagramPacket(sendData, sendData.length,
                                IPAddress, faultManagerPort);
                clientSocket.send(sendPacket);
                DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
                clientSocket.receive(receivePacket);
                //create a string object from the byte array
                String modifiedSentence = new String(receivePacket.getData());
                HashMap<String, Node> tempList = new HashMap<String, Node>();
                if (modifiedSentence != null && !"".equals(modifiedSentence)) {
                    int index = modifiedSentence.indexOf('-');
                    String dataString = modifiedSentence.substring(index + 1);
                    String values[] = dataString.split("\\---");
                    for (String value : values) {
                        if (value == null || "".equals(value)) {
                            continue;
                        }
                        String attributes[] = value.split("\\,");
                        if (attributes.length < 2) {
                            continue;
                        }
                        //Here we only consider Type==1 (AppServers)
                        if(Integer.parseInt(attributes[1]) == 2) {
                            continue;
                        }
                        Node node = new Node();
                        node.setCpu(Integer.parseInt(attributes[2]));
                        node.setIP(attributes[0]);
                        tempList.put(node.getIP(), node);
                    }
                }
                process(tempList);
                clientSocket.close();
                Thread.sleep(1000);
            } catch (Exception e) {
            }
        }
    }

    /**
     * Process the list and find which servers are new and which are removed
     * @param list
     */
    private void process(HashMap<String, Node> list) {
        ArrayList<String> toAdd = new ArrayList<String>();
        ArrayList<String> toDelete = new ArrayList<String>();
        Iterator<String> keys = list.keySet().iterator();
        while (keys.hasNext()) {
            String key = keys.next();
            if (serverList.get(key) == null) {
                //a new server, so we need to add it as an appServer
                toAdd.add(key);
            }
        }

        Iterator<String> serverKeys = serverList.keySet().iterator();
        while (serverKeys.hasNext()) {
            String key = serverKeys.next();
            if (list.get(key) == null) {
                //This server is failed, so need to remove from the list
                toDelete.add(key);
            }
        }

        //removing application servers from the list of servers
        for (int i = 0; i < toDelete.size(); i++) {
            logbasedExecutor.removeAppServer(toDelete.get(i));
            serverList.remove(toDelete.get(i));
        }
        //Adding application servers to the system
        for (int i = 0; i < toAdd.size(); i++) {
            AddressBean bean = new AddressBean();
            bean.setIp(toAdd.get(i));
            bean.setPort(appServerPort);
            logbasedExecutor.addAppServer(bean, bean.getIp());
            serverList.put(toAdd.get(i), list.get(toAdd.get(i))) ;
        }
    }
}
